文章参考:刨根问底,kafka到底会不会丢消息_kafka_爱笑的架构师_InfoQ写作社区
三种消息传递语义
Kafka 中消息传递语义主要分为以下三种:
- At most once(最多一次):消息可能会丢失,但绝不会重复传输。
- At least once(至少一次):消息绝不会丢失,但可能会重复传输。
- Exactly once(精确一次):消息绝不会丢失,且只会被传输一次。
消息丢失场景分析
1. Producer 端
Producer 端的消息丢失与 ACK (Acknowledge) 机制紧密相关。
三种 ACK 配置
- 0:Producer 发送消息后不等待任何确认(Fire and Forget)。
- 1:Leader 副本将消息写入本地日志后即返回 ACK,不等待 Follower 同步。
- all (-1):Leader 等待所有 ISR(In-Sync Replicas)集合中的副本同步完成后才返回 ACK。
是否会丢消息?
- ACK = 0:会丢。发送出去即视为成功,若发送过程中网络故障或 Broker 宕机,消息直接丢失。
- ACK = 1:会丢。如果 Leader 接收消息并 ACK 后,在同步给 Follower 之前挂掉,此时集群选举了未同步该消息的 Follower 成为新 Leader,则该消息丢失。
- ACK = all:不丢(配合
min.insync.replicas参数配置得当的情况下)。
2. Broker 端
- 会丢:Kafka 为了高性能,消息是先写入操作系统的 Page Cache。如果数据写入 Cache 后,在操作系统进行刷盘(fsync)之前机器宕机断电,内存中的数据将会丢失。
3. Consumer 端
Consumer 端的消息丢失取决于 Offset Commit(提交偏移量) 的时机。
两种 Commit 场景
- 先消费再 Commit:不丢。
- 逻辑:获取消息 -> 执行业务逻辑 -> 提交 Offset。
- 风险:如果消费成功但在 Commit 前宕机,重启后会重新拉取该消息,导致重复消费。
- 先 Commit 再消费:会丢。
- 逻辑:获取消息 -> 提交 Offset -> 执行业务逻辑。
- 风险:如果 Commit 成功但业务逻辑执行失败(或服务宕机),该消息已被标记为“已消费”,实际业务未处理,导致丢失。
总结与最佳实践
- Broker 端:极端情况下(Page Cache 刷盘间隙)会丢消息,且物理层面上无法完全避免(除非同步刷盘,但这会极大牺牲吞吐量)。
- P/C 端(Producer/Consumer):可以通过配置(acks=all)和代码逻辑(先消费后提交)避免丢失,但这通常会带来重复消费的问题。
- 核心权衡:需要在 消息不丢失 与 系统吞吐量/性能 之间做权衡。
- 最佳实践:永远不要完全依赖中间件保证 100% 可靠性。业务侧做好补偿机制(如本地消息表、日志记录、定期对账等),做好消息丢失的兜底方案。